home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / misc / volume4 / strtod < prev    next >
Encoding:
Internet Message Format  |  1989-02-03  |  7.9 KB

  1. Path: xanth!mcnc!gatech!cwjcc!hal!ncoast!allbery
  2. From: ok@quintus.UUCP
  3. Newsgroups: comp.sources.misc
  4. Subject: v04i013: Replacement for strtod()
  5. Message-ID: <232@quintus.UUCP>
  6. Date: 3 Aug 88 01:19:11 GMT
  7. Sender: allbery@ncoast.UUCP
  8. Reply-To: ok@quintus.UUCP
  9. Organization: Quintus Computer Systems, Inc.
  10. Lines: 218
  11. Approved: allbery@ncoast.UUCP
  12.  
  13. Posting-number: Volume 4, Issue 13
  14. Submitted-by: "A. Nonymous" <ok@quintus.UUCP>
  15. Archive-name: strtod
  16.  
  17. strtod() is a nice function, but it's often missing or broken.
  18. Here is a replacement for it which does the parsing and checking
  19. required of strtod() but calls atof() to do the conversion.
  20.  
  21. ---- cut here ---- cut here ---- cut here ---- cut here ---- cut here ----
  22. #!/bin/sh
  23. # This is a shell archive, meaning:                              
  24. # 1. Remove everything above the #!/bin/sh line.                
  25. # 2. Save the resulting test in a file.                          
  26. # 3. Execute the file with /bin/sh (not csh) to create the files:
  27. #    README
  28. #    makefile
  29. #    str2dbl.c
  30. sed -e 's/^X//' >README <<'------ EOF ------'
  31. XFILES:
  32. X    README        - this file
  33. X    makefile    - a trivial makefile
  34. X    str2dbl.c    - defines str2dbl(), requires atof() + float support
  35. X
  36. Xdouble str2dbl(char *str, char **ptr)
  37. X    converts the character string str points to to a double-precision
  38. X    floating-point number and returns it.  str2dbl() recognises
  39. X
  40. X    <space>... [+|-] <digit>... [. <digit>...] [<exponent>]
  41. X
  42. X    where <exponent> is
  43. X
  44. X    e|E [+|-| ] <digit> <digit>...
  45. X
  46. X    If ptr is not (char**)NULL, *ptr is assigned a pointer to the
  47. X    character just after the last character accepted by str2dbl().
  48. X    {This will typically be a layout character or NUL.}
  49. X
  50. X    If there aren't any digits at the front (e.g. the input is
  51. X    "e45" or "Fred" or "-gotcha-") str2dbl() will make *ptr point
  52. X    to the whole of str (even if there were leading spaces and signs)
  53. X    and will return 0.0.
  54. X
  55. X    If there is some problem with the exponent (e.g. the input is
  56. X    "1.2e%45") str2dbl() will reject all of the exponent, making *ptr
  57. X    point to the 'e', and will convert only the preceding characters.
  58. X
  59. X    A single space after the 'e' or 'E' of an exponent is accepted as
  60. X    if it were a '+' sign, because some output formatting routines
  61. X    generate numbers that look like that.  Spaces are not otherwise
  62. X    accepted inside numbers.
  63. X
  64. X    If the correct value cannot be represented, str2dbl() sets errno
  65. X    to ERANGE, and returns +/-HUGE for overflow, +/-ZERO for underflow.
  66. X
  67. XWARNING:
  68. X    The source code as provided is set up for 64-bit IEEE-754 floating
  69. X    point.  VAX and IBM floating-point formats are different, so the
  70. X    numeric range is different.  Some machines which look as though
  71. X    they have IEEE floats may not support infinities or denormalised
  72. X    numbers, in which case the numeric range is different.  You will
  73. X    have to determine the correct values for your machine.
  74. X
  75. ------ EOF ------
  76. ls -l README
  77. sed -e 's/^X//' >makefile <<'------ EOF ------'
  78. Xstr2dbl.o: str2dbl.c
  79. ------ EOF ------
  80. ls -l makefile
  81. sed -e 's/^X//' >str2dbl.c <<'------ EOF ------'
  82. X/*  File   : str2dbl.c
  83. X    Author : Richard A. O'Keefe @ Quintus Computer Systems, Inc.
  84. X    Updated: Tuesday August 2nd, 1988
  85. X    Defines: double str2dbl(char *str, char**ptr)
  86. X*/
  87. X
  88. X/*  This is an implementation of the strtod() function described in the 
  89. X    System V manuals, with a different name to avoid linker problems.
  90. X    All that str2dbl() does itself is check that the argument is well-formed
  91. X    and is in range.  It leaves the work of conversion to atof(), which is
  92. X    assumed to exist and deliver correct results (if they can be represented).
  93. X
  94. X    There are two reasons why this should be provided to the net:
  95. X    (a) some UNIX systems do not yet have strtod(), or do not have it
  96. X        available in the BSD "universe" (but they do have atof()).
  97. X    (b) some of the UNIX systems that *do* have it get it wrong.
  98. X    (some crash with large arguments, some assign the wrong *ptr value).
  99. X    There is a reason why *we* are providing it: we need a correct version
  100. X    of strtod(), and if we give this one away maybe someone will look for
  101. X    mistakes in it and fix them for us (:-).
  102. X*/
  103. X    
  104. X/*  The following constants are machine-specific.  MD{MIN,MAX}EXPT are
  105. X    integers and MD{MIN,MAX}FRAC are strings such that
  106. X    0.${MDMAXFRAC}e${MDMAXEXPT} is the largest representable double,
  107. X    0.${MDMINFRAC}e${MDMINEXPT} is the smallest representable +ve double
  108. X    MD{MIN,MAX}FRAC must not have any trailing zeros.
  109. X    The values here are for IEEE-754 64-bit floats.
  110. X    It is not perfectly clear to me whether an IEEE infinity should be
  111. X    returned for overflow, nor what a portable way of writing one is,
  112. X    so HUGE is just 0.MAXFRAC*10**MAXEXPT (this seems still to be the
  113. X    UNIX convention).
  114. X
  115. X    I do know about <values.h>, but the whole point of this file is that
  116. X    we can't always trust that stuff to be there or to be correct.
  117. X*/
  118. Xstatic    int    MDMINEXPT    = {-323};
  119. Xstatic    char    MDMINFRAC[]    = "494065645841246544";
  120. Xstatic    double    ZERO        = 0.0;
  121. X
  122. Xstatic    int    MDMAXEXPT    = { 309};
  123. Xstatic    char    MDMAXFRAC[]    = "17976931348623147";
  124. Xstatic    double    HUGE        = 1.7976931348623147e308;
  125. X
  126. Xextern    double    atof();        /* Only called when result known to be ok */
  127. X
  128. X#include <errno.h>
  129. Xextern    int    errno;
  130. X
  131. Xdouble str2dbl(str, ptr)
  132. X    char *str;
  133. X    char **ptr;
  134. X    {
  135. X    int sign, scale, dotseen;
  136. X    int esign, expt;
  137. X    char *save;
  138. X    register char *sp, *dp;
  139. X    register int c;
  140. X    char *buforg, *buflim;
  141. X    char buffer[64];        /* 45-digit significand + */
  142. X                    /* 13-digit exponent */
  143. X    sp = str;
  144. X    while (*sp == ' ') sp++;
  145. X    sign = 1;
  146. X    if (*sp == '-') sign -= 2, sp++;
  147. X    dotseen = 0, scale = 0;
  148. X    dp = buffer;    
  149. X    *dp++ = '0'; *dp++ = '.';
  150. X    buforg = dp, buflim = buffer+48;
  151. X    for (save = sp; c = *sp; sp++)
  152. X        if (c == '.') {
  153. X        if (dotseen) break;
  154. X        dotseen++;
  155. X        } else
  156. X        if ((unsigned)(c-'0') > (unsigned)('9'-'0')) {
  157. X        break;
  158. X        } else
  159. X        if (c == '0') {
  160. X        if (dp != buforg) {
  161. X            /* This is not the first digit, so we want to keep it */
  162. X            if (dp < buflim) *dp++ = c;
  163. X        } else {
  164. X            /* No non-zero digits seen yet */
  165. X            /* If a . has been seen, scale must be adjusted */
  166. X            if (dotseen) scale--;
  167. X        }
  168. X        } else {
  169. X        /* This is a nonzero digit, so we want to keep it */
  170. X        if (dp < buflim) *dp++ = c;
  171. X        /* If it precedes a ., scale must be adjusted */
  172. X        if (!dotseen) scale++;
  173. X        }
  174. X    if (sp == save) {
  175. X        if (ptr) *ptr = str;
  176. X        errno = EDOM;        /* what should this be? */
  177. X        return ZERO;
  178. X    }
  179. X    
  180. X    while (dp > buforg && dp[-1] == '0') --dp;
  181. X    if (dp == buforg) *dp++ = '0';
  182. X    *dp = '\0';
  183. X    /*  Now the contents of buffer are
  184. X        +--+--------+-+--------+
  185. X        |0.|fraction|\|leftover|
  186. X        +--+--------+-+--------+
  187. X             ^dp points here
  188. X        where fraction begins with 0 iff it is "0", and has at most
  189. X        45 digits in it, and leftover is at least 16 characters.
  190. X    */
  191. X    save = sp, expt = 0, esign = 1;
  192. X    do {
  193. X        c = *sp++;
  194. X        if (c != 'e' && c != 'E') break;
  195. X        c = *sp++;
  196. X        if (c == '-') esign -= 2, c = *sp++; else
  197. X        if (c == '+' || c == ' ') c = *sp++;
  198. X        if ((unsigned)(c-'0') > (unsigned)('9'-'0')) break;
  199. X        while (c == '0') c = *sp++;
  200. X        for (; (unsigned)(c-'0') <= (unsigned)('9'-'0'); c = *sp++)
  201. X        expt = expt*10 + c-'0';        
  202. X        if (esign < 0) expt = -expt;
  203. X        save = sp-1;
  204. X    } while (0);
  205. X    if (ptr) *ptr = save;
  206. X    expt += scale;
  207. X    /*  Now the number is sign*0.fraction*10**expt  */
  208. X    errno = ERANGE;
  209. X    if (expt > MDMAXEXPT) {
  210. X        return HUGE*sign;
  211. X    } else
  212. X    if (expt == MDMAXEXPT) {
  213. X        if (strcmp(buforg, MDMAXFRAC) > 0) return HUGE*sign;
  214. X    } else
  215. X    if (expt < MDMINEXPT) {
  216. X        return ZERO*sign;
  217. X    } else
  218. X    if (expt == MDMINEXPT) {
  219. X        if (strcmp(buforg, MDMINFRAC) < 0) return ZERO*sign;
  220. X    }
  221. X    /*  We have now established that the number can be  */
  222. X    /*  represented without overflow or underflow  */
  223. X    (void) sprintf(dp, "E%d", expt);
  224. X    errno = 0;
  225. X    return atof(buffer)*sign;
  226. X    }
  227. X
  228. ------ EOF ------
  229. ls -l str2dbl.c
  230. exit 0
  231.